<script>
// default icons
const icons = {
  default: 'fa fa-institution'
}

function closest (event, tag, until) {
  for (var p of event.path) {
    var tagName = p.tagName.toLowerCase()
    if (tagName == tag || tagName == until) {
      return p
    }
  }
  return null
}

// default fetures
export const fetures = {
  'wholerow': 1,
  'checkbox': 1,
  'multiple': 0,
  'striped': 0,
  'radio': 0,
  'dots': 0
}

export default {
  name: 'jstree',
  props: {
    value: null,
    nodes: {
      required: true,
      type: Array
    }
  },
  data: () => ({
    checked: {},
    map: {}
  }),
  methods: {
    triggerEvents () {
      var nodes = Object.values(this.checked)
      if (this.$listeners.input) {
        var keys = Object.keys(this.checked)
        var len = keys.length
        var value = len ? keys[0] : ''
        switch (typeof this.value) {
          case 'array' :
            value = keys
            break
          case 'number':
            value = len ? Number(keys[0]) : ''
            break
        }
        this.$emit('input', value)
      }
      if (this.$listeners.change) {
        this.$emit('change', nodes)
      }
    },
    selectNodes (node) {
      var exists = this.checked[node.id]
      if (exists) {
        this.$delete(this.checked, node.id)
      } else {
        if (!this.options.multiple) {
          this.checked = {}
        }
        this.$set(this.checked, node.id + '', node)
      }
      this.triggerEvents()
    }
  },
  created () {
    this.options = {}
    this.fetures = Object.keys(fetures).filter(k => {
      var on = fetures[k] || k in this.$attrs
      this.options[k] = on
      return on
    })
  },
  mounted () {
    // wholerow enabled, add hover events for toggle wholerow class
    if (this.options.wholerow) {
      var el
      this.$el.addEventListener('mouseover', function (e) {
        el = closest(e, 'li')
        el.classList.add('jstree-hover')
      })
      this.$el.addEventListener('mouseout', function () {
        // no need for query here, just use it
        el.classList.remove('jstree-hover')
      })
    }
  },
  updated () {
    this.triggerEvents()
  },
  render (h) {
    var v = this

    function mapfn (node, index, array) {
      v.map[node.id] = node                     // map node by it's id
      if (node.checked || node.id == v.value) {
        v.checked[node.id] = node
      }
      var checked = v.checked[node.id]          // should current node been checked
      var parent = node.children                // is parent
      var last = index == array.length - 1      // is last node
      var arr = []                              // children array of current node
      // wholerow

      if (v.options.wholerow) {
        arr.push(h('div', {class: 'jstree-row'}))
      }
      // toggle, try these: ▶►▷◁◀◄
      arr.push(h('svg', {class: 'jstree-icon jstree-ocl', attrs: {viewBox: '0 -150 940 1324', xmlns: 'http://www.w3.org/2000/svg'}}, [
        h('path', {attrs: {d: 'M635.31088 512q0 14.848-10.848 25.728l-256 256q-10.848 10.848-25.728 10.848t-25.728-10.848-10.848-25.728l0-512q0-14.848 10.848-25.728t25.728-10.848 25.728 10.848l256 256q10.848 10.848 10.848 25.728z'}})
      ]))
      // anchor sub elements
      var sub = []
      // checkbox
      sub.push(h('input', {class: 'jstree-check', attrs: {type: 'checkbox', value: node.id, checked}}))
      sub.push(h('i', {class: 'jstree-icon'}))
      // icon
      sub.push(h('i', {class: 'jstree-icon jstree-themeicon ' + (node.icon || icons.default)}))
      // name
      sub.push(v._v(node.name))
      arr.push(h('a', {class: 'jstree-anchor'}, sub))
      // children
      if (parent) {
        arr.push(h('ul', node.children.map(mapfn)))
      }
      return h('li', {
        class: ['jstree-node',
          parent ? 'jstree-parent' : 'jstree-leaf',
          checked ? 'jstree-checked' : '',
          node.opened ? 'jstree-open' : '',
          last ? 'jstree-last' : ''
        ],
        on: {
          click (e) {
            var target = e.target
            if (/svg|path/i.test(target.tagName)) {        // click inside the svg icon
              node.opened = !node.opened                   // toggle open state
            } else {
              v.selectNodes(node)
            }
            e.stopPropagation()
          }
        }
      }, arr)
    }

    return h('ul', {class: 'jstree-root-ul' + ' jstree-' + this.fetures.join(' jstree-')}, this.nodes.map(mapfn))
  }
}
</script>

<style lang="scss">
  /* variables */
  $hover-bg-color: rgba(95, 190, 170, 0.2);
  $active-bg-color: rgba(95, 190, 170, 0.4);
  $hover-shadow-color: #ccc;
  $active-shadow-color: #999;
  $disabled-bg-color: #efefef;
  $disabled-color: #666;
  $size: 1.5rem;
  // sizing
  .jstree-node {
    line-height: $size;
    min-height: $size;
    min-width: $size
  }
  .jstree-anchor,
  .jstree-icon {
    line-height: $size;
    height: $size
  }
  .jstree-anchor { height: $size }
  .jstree-icon { width: $size }
  // ul
  .jstree-root-ul {
    position: relative;
    list-style: none;
    padding: 0;
    margin: 0;
    ul {
      padding-left: $size;
      list-style: none;
      display: none;
    }
  }
  .jstree-node { white-space: nowrap }
  .jstree-anchor, .jstree-icon {
    text-decoration: none;
    vertical-align: top;
    position: relative;
    padding: 0;
    margin: 0;
  }
  .jstree-anchor {
    display: inline-flex;
    align-items: center;
    padding: 0 .25rem 0 .0625rem;
    white-space: nowrap;
    cursor: pointer;
    color: inherit;
    outline: 0;
  }
  .jstree-anchor:hover,
  .jstree-hover > .jstree-row {
    background: $hover-bg-color
  }
  .jstree-checked {
    > .jstree-anchor,
    > .jstree-row { background: $active-bg-color }
  }
  .jstree-icon { display: inline-block; text-align: center }
  .jstree-ocl {
    transition: transform .2s, fill .2s;
    visibility: hidden;
    cursor: pointer;
    fill: transparent;
    stroke-linejoin: round;
    stroke-width: 1rem;
    stroke: #000;
  }
  .jstree-loading > .jstree-ocl {
    background: url('') center no-repeat
  }
  .jstree-leaf > .jstree-ocl { cursor: default }
  .jstree-open > .jstree-ocl {
    transform: rotate(45deg);
    fill: currentColor
  }
  .jstree-parent {
    > .jstree-ocl { visibility: visible }
    &.jstree-open > ul { display: block }
  }
  .jstree-themeicon { margin-right: 2px }
  .jstree-no-icons .jstree-themeicon { display: none }
  .jstree-animated,
  .jstree-anchor,
  .jstree-row {
    transition: background-color 0.15s, box-shadow 0.15s
  }
  // disabled
  .jstree-disabled {
    color: $disabled-color;
    > .jstree-anchor,
    > .jstree-hover,
    > .jstree-row {
      background: $disabled-bg-color;
      cursor: default;
    }
    > .jstree-icon {
      opacity: 0.8
    }
  }
  // ellipsis
  .jstree-ellipsis {
    overflow: hidden;
    &.jstree-no-icons .jstree-anchor {width: calc(100% - 5px)}
    .jstree-anchor {
      width: calc(100% - (#{$size} + 5px));
      text-overflow: ellipsis;
      overflow: hidden
    }
  }
  // checkboxes
  .jstree-check {
    display: none;
    &:checked + i {border: 1px solid currentColor}
    &:disabled + i { opacity: .3 }
    & + i {
      display: none;
      border-radius: .125rem;
      border: .0625rem solid #bbb;
      transition: all .2s;
      position: relative;
      cursor: pointer;
      height: $size - .5rem;
      width: $size - .5rem;
      margin: .25rem;
    }
    & + i:hover { border-color: currentColor }
    & + i:after {
      transition: background .2s;
      position: absolute;
      content: '';
      left: 50%;
      top: 50%
    }
  }
  .jstree-checkbox {
    .jstree-check + i { display: inline-block }
    .jstree-check + i:after {
      border: solid transparent;
      border-radius: 1px;
      border-width: 0 .125rem .125rem 0;
      transform: rotate(45deg) translate(-110%, -25%);
      height: .7rem;
      width: .4rem;
    }
    .jstree-check:checked + i:after {border-color: currentColor}
  }
  .jstree-radio {
    .jstree-check + i { display: inline-block; border-radius: 20px }
    .jstree-check:checked + i:after {
      transform: translate(-50%, -50%);
      background: currentColor;
      border-radius: 50%;
      padding: .25rem
    }
  }
  // wholerow
  .jstree-wholerow {
    display: inline-block;
    min-width: 100%;
    .jstree-row {
      position: absolute;
      user-select: none;
      cursor: pointer;
      height: $size;
      width: 100%;
      left: 0;
    }
    .jstree-checked > .jstree-anchor,
    .jstree-anchor:hover { background: none }
  }
  // stripes
  .jstree-striped {
    background: url("");
    background-size: auto ($size * 2)
  }
</style>
